import { Component, OnInit, OnDestroy, TemplateRef, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { Subscription, of, Observable } from 'rxjs'
import { switchMap, take, tap } from 'rxjs/operators'

import { IBaseInfoColumn } from '@shared/interfaces/baseInfo'
import {
  Settlement,
  StoreSettlementUpdate,
  Store,
  StoreFull,
  ESettleType,
  SettleTypeMap,
  SettleTimeMap,
  ESettleTime,
  SettleTime
} from '@shared/interfaces/store'
import { PageService } from '@shared/services/page.service'
import { ETabScrollStatus, ECustomAcl } from '@shared/enums'
import { DataService } from '@shared/services/data.service'
import { DetailTabClass } from '@shared/classes/DetailTabClass'
import { StoreService } from '../../store.service'
import { Validators } from 'app/utils/validator'
import {
  branchCodeMaxLength,
  branchCodeReg,
  bankAccountMaxLength,
  bankAccountReg,
  bankAccountNameMaxLength,
  bankMaxLength,
} from 'app/utils/contraints'
import { I18NService } from '@core'
import { CustomACLService } from '@shared/services/acl.service'
import { IOption } from '@shared/interfaces/common'
import { deepCopy } from '@delon/util'
import { HelperService } from '@shared/services/helper.service'

@Component({
  selector: 'app-store-detail-tab-settlement',
  templateUrl: './tab-settlement.component.html',
  styleUrls: ['./tab-settlement.less'],
})
export class StoreDetailTabSettlementComponent extends DetailTabClass<Settlement> implements OnInit, OnDestroy {

  constructor(
    private dataService: DataService,
    private storeService: StoreService,
    public pageService: PageService,
    public route: ActivatedRoute,
    private i18n: I18NService,
    private customACLService: CustomACLService,
    private helperService: HelperService,
  ) {
    super(pageService, route)
  }

  get isNeedDiff(): boolean {
    return this.dataService.isNeedDiff
  }
  @ViewChild('tpIsSupportDualMessage') tpIsSupportDualMessage: TemplateRef<void>
  @ViewChild('tpSettleTime') tpSettleTime: TemplateRef<void>
  @ViewChild('tpSettleType') tpSettleType: TemplateRef<void>
  @ViewChild('tpSettleDays') tpSettleDays: TemplateRef<void>

  @ViewChild('dualOperate') dualOperate: TemplateRef<void>
  @ViewChild('settleCycleOperate') settleCycleOperate: TemplateRef<void>

  settleTypeOptions: IOption[] = []
  settleTimeOptions: IOption[] = []
  disabled = false
  min = 1
  max = 14
  step = 1
  precision = 0

  isLoading = true

  private subscription: Subscription
  private store: Store

  @ViewChild('accountOperate') accountOperate: TemplateRef<any>

  fields = {
    Account: '1',
    DualMessage: '2',
    SettleCycle: '3'
  }

  ngOnInit(): void {
    this.initSettleTypeOptions()
    this.initSettleTimeOptions()
    this.initialColumns()
    this.initAclAbility()
    this.initSubscription()
    this.initialData()
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe()
    this.customACLService.removeACLAbility(ECustomAcl.StoreEditable)
    this.customACLService.removeACLAbility(ECustomAcl.AcquirerSupportDualMessage)
  }

  initAclAbility(): void {
    this.customACLService.addACLAbility(ECustomAcl.StoreEditable)
    this.customACLService.addACLAbility(ECustomAcl.AcquirerSupportDualMessage)
  }

  initSubscription(): void {
    this.pageService.isStoreDisplayInTask$.pipe(
      tap(isStoreDisplayInTask => {
        if (isStoreDisplayInTask) {
          this.customACLService.removeACLAbility(ECustomAcl.StoreEditable)
        }
      }),
      switchMap(() => this.pageService.isStoreLocked$),
      tap(isStoreLocked => {
        if (isStoreLocked) {
          this.customACLService.removeACLAbility(ECustomAcl.StoreEditable)
        }
      }),
    ).subscribe()
  }

  initialData() {
    this.isLoading = true
    const { origStore, currentStore } = this.dataService
    if (this.isNeedDiff) this.origData = origStore.common.settlement
    this.subscription = this.dataService.storeFull$.value$
      .pipe(
        take(1),
        switchMap(full => (this.isNeedDiff ? of(currentStore) : of(full.store))),
        tap(store => {
          this.store = deepCopy(store)
          this.data = deepCopy(store!.common!.settlement)
          this.setEditableOfSettleDays()
          this.setEdit(this.fields.Account, 4)
          this.setEdit(this.fields.DualMessage, 1)
          this.setEdit(this.fields.SettleCycle, 2)
        }),
        switchMap(() => this.storeService.getAcquirerDualMessageSwitch(this.store.common.insCode)),
        tap(isSupportDualMessage => {
          if (!isSupportDualMessage || !this.data.isSupportDualMessage) {
            this.customACLService.removeACLAbility(ECustomAcl.AcquirerSupportDualMessage)
          }
        }),
        switchMap(() => {
          return this.createAccountColumns(this.data)
            .pipe(switchMap(this.checkColumnsDiff))
        }),
        tap(columns => {
          this.columns[this.fields.Account].push(columns)
        }),
        switchMap(() => {
          return this.createDualMessageColumns(this.data)
            .pipe(switchMap(this.checkColumnsDiff))
        }),
        tap(columns => {
          this.columns[this.fields.DualMessage].push(columns)
        }),
        switchMap(() => {
          return this.createSettleCycleColumns(this.data)
            .pipe(switchMap(this.checkColumnsDiff))
        }),
        tap(columns => {
          this.columns[this.fields.SettleCycle].push(columns)
          this.setupIdForColumn(this.fields)
          this.isLoading = false
        })
      ).subscribe()
    this.pageService.setTabScrollStatus(ETabScrollStatus.Scroll)
  }

  private createDualMessageColumns(settlement: Settlement = {} as Settlement): Observable<IBaseInfoColumn[]> {
    const columns: IBaseInfoColumn[] = [
      {
        span: 12,
        label: this.labelFeature(),
        index: 'isSupportDualMessage',
        value: this.parseBoolToString(settlement.isSupportDualMessage),
        displayValue: this.renderSingleOrDual(settlement.isSupportDualMessage),
        template: this.tpIsSupportDualMessage,
      },
      {
        span: 12,
        label: this.trans('BIZ.STORE.OTHER.SETTLEMENT.SETTLE_TIME'),
        index: 'settleTime',
        displayValue: this.parseSettleTime(settlement.settleTime as SettleTime),
        template: this.tpSettleTime,
        acl: [ECustomAcl.AcquirerSupportDualMessage],
        aclMode: 'allOf',
      },
      /** 屏蔽对门店单双信息模式修改的操作 */
      // {
      //   span: 4,
      //   label: 'GENERAL.INFO.OPERATION',
      //   labelI18n: 'GENERAL.INFO.OPERATION',
      //   template: this.dualOperate,
      //   operate: true,
      //   acl: ['api.put.camel.institution.one.trade', ECustomAcl.StoreEditable, ECustomAcl.AcquirerSupportDualMessage],
      //   aclMode: 'allOf',
      // }
    ]
    return of(columns)
  }
  private createSettleCycleColumns(settlement: Settlement = {} as Settlement): Observable<IBaseInfoColumn[]> {
    const columns: IBaseInfoColumn[] = [
      {
        span: 12,
        label: '清算模式',
        labelI18n: 'BIZ.STORE.OTHER.SETTLEMENT.MODE',
        value: settlement.settleType,
        index: 'settleType',
        displayValue: this.parseSettleTypeValue(settlement.settleType),
        template: this.tpSettleType,
      },
      {
        span: 12,
        label: '清算周期(天)',
        labelI18n: 'BIZ.STORE.OTHER.SETTLEMENT.PERIOD',
        value: settlement.settleCycleDays,
        index: 'settleCycleDays',
        template: this.tpSettleDays,
      }
    ]
    return of(columns)
  }

  private createAccountColumns(settlement: Settlement = {} as Settlement): Observable<IBaseInfoColumn[]> {
    const columns: IBaseInfoColumn[] = [
      {
        span: 5,
        label: 'BIZ.STORE.OTHER.SETTLEMENT.ACCOUNT_NAME',
        labelI18n: 'BIZ.STORE.OTHER.SETTLEMENT.ACCOUNT_NAME',
        index: 'bankAccountName',
        value: settlement.bankAccountName,
        validator: {
          funcs: [Validators.maxLength(bankAccountNameMaxLength)],
          errorTips: [this.trans('VALIDATION.MAX_LENGTH', { length: bankAccountNameMaxLength })],
        },
      },
      {
        span: 5,
        label: 'BIZ.STORE.OTHER.SETTLEMENT.B ANK',
        labelI18n: 'BIZ.STORE.OTHER.SETTLEMENT.BANK',
        index: 'bank',
        value: settlement.bank,
        validator: {
          funcs: [Validators.maxLength(bankMaxLength)],
          errorTips: [this.trans('VALIDATION.MAX_LENGTH', { length: bankMaxLength })],
        },
      },
      {
        span: 5,
        label: 'BIZ.STORE.OTHER.SETTLEMENT.BANK_CO DE_SWIFT',
        labelI18n: 'BIZ.STORE.OTHER.SETTLEMENT.BANK_CODE_SWIFT',
        index: 'bankCodeSWIFT',
        value: settlement.bankCodeSWIFT,
        validator: {
          funcs: [Validators.maxLength(branchCodeMaxLength), Validators.pattern(branchCodeReg)],
          errorTips: [
            this.trans('VALIDATION.MAX_LENGTH', { length: branchCodeMaxLength }),
            this.trans('VALIDATION.PATTERN.ALPHANUMERIC_SYMBOLS_ONLY'),
          ],
        },
      },
      {
        span: 5,
        label: 'BIZ.STORE.OTHER.SETTL EMENT.ACCOUNT',
        labelI18n: 'BIZ.STORE.OTHER.SETTLEMENT.ACCOUNT',
        index: 'bankAccount',
        value: settlement.bankAccount,
        validator: {
          funcs: [Validators.maxLength(bankAccountMaxLength), Validators.pattern(bankAccountReg)],
          errorTips: [
            this.trans('VALIDATION.MAX_LENGTH', { length: bankAccountMaxLength }),
            this.trans('VALIDATION.PATTERN.ALPHANUMERIC_SYMBOLS_ONLY'),
          ],
        },
      },
      {
        span: 4,
        label: 'GENERAL.INFO.OPERATION',
        labelI18n: 'GENERAL.INFO.OPERATION',
        template: this.accountOperate,
        operate: true,
        acl: ['api.put.camel.store.one.settlement', ECustomAcl.StoreEditable],
        aclMode: 'allOf',
      },
    ]
    return of(columns)
  }

  checkColumnsDiff = (columns: IBaseInfoColumn[]): Observable<IBaseInfoColumn[]> => {
    /** 如果不需要Diff，直接返回false */
    const { isNeedDiff } = this.dataService
    if (!isNeedDiff) return of(columns)
    return this.getDiffedColumns(columns)
  }

  // abstract methods
  onCancel() {
    this.initialColumns()
    this.initialData()
  }

  onSubmit() {
    const columns = this.columns[this.field][this.rowIndex]
    const d = new StoreSettlementUpdate(this.store.version)
    columns.filter(col => !!col.index).forEach(col => {
      if (col.index === 'isSupportDualMessage') {
        this.data[col.index] = this.parseStringToBool(col.value as string)
        return
      }
      this.data[col.index] = col.value
    })
    d.settlement = this.data
    const token = this.dataService.token

    this.storeService
      .updateSettlement(this.store.common.intStoreCode, token, d)
      .pipe(take(1))
      .subscribe(result => {
        this.pageService.setStoreBeenModified(true)
        this.isEdits[this.field][this.rowIndex] = false
        if (result instanceof StoreFull) {
          this.dataService.setStoreFull(result)
          this.initialColumns()
          this.initialData()
          if (result.store.version) {
            this.store.version = result.store.version
          }
          return
        }
        /** 进审核的情况 */
        this.store.common.settlement = this.data
        const storeFull = this.dataService.storeFull$.value
        storeFull.store = this.store
        this.dataService.setStoreFull(storeFull)
      })
  }

  switchIsSupportDualMessage(value: string): void {
    this.columns[this.fields.DualMessage][0][0].displayValue = this.renderSingleOrDual(this.parseStringToBool(value))
  }

  selectSettleType(settleType: ESettleType): void {
    this.data.settleType = settleType
    switch (settleType) {
      case ESettleType.T1:
        this.disabled = true
        this.data.settleCycleDays = 1
        break

      case ESettleType.Delay:
        this.disabled = false
        break

      case ESettleType.AddUp:
        this.disabled = false
        break
    }
  }

  selectSettleTime(settleTime: ESettleTime): void {
    this.data.settleTime = settleTime
  }

  changeSettleDays(settleCycleDays: number): void {
    this.data.settleCycleDays = settleCycleDays
  }

  initialColumns() {
    /*
     * 这个columns是用来收集所有需要display在这个tab里面的数据.
     * 比如 "交易币种转换" = '1', 然后交易币种转换里有一个Array = [商户交易币种, 转换交易币种, 转换汇率, 等等] 然后他在columns里就是
     * {'1': [[{商户交易币种, 转换交易币种, 转换汇率, 等等}]]}
     */
    // 现在这个trades component需要展示2个field. 这里创建2个空的Array在this.columns里的field
    for (const key in this.fields) {
      this.columns[this.fields[key]] = []
    }
  }

  private trans(key: string, params?: Object): string {
    return this.i18n.fanyi(key, params)
  }

  private renderSingleOrDual(value: boolean): string {
    return value ? this.trans('BIZ.ACQUIRER.ACQUIRER_FUNC.DUAL_MESSAGE') : this.trans('BIZ.ACQUIRER.ACQUIRER_FUNC.SINGLE_MESSAGE')
  }

  private setEditableOfSettleDays(): void {
    this.disabled = this.data.settleType === ESettleType.T1 ? true : false
  }

  private initSettleTypeOptions(): void {
    this.settleTypeOptions = this.helperService.generateOptionsFromEnum(SettleTypeMap)
  }

  private initSettleTimeOptions(): void {
    this.settleTimeOptions = this.helperService.generateOptionsFromEnum(SettleTimeMap)
  }

  private parseSettleTypeValue(settleType: ESettleType): string {
    try {
      return this.trans(SettleTypeMap[settleType])
    } catch (e) {
      return this.trans('GENERAL.INFO.NONE')
    }
  }

  private parseSettleTime(settleTime: SettleTime): string {
    // 判空处理
    if (!settleTime || Object.keys(settleTime).length === 0) return null
    let time = ESettleTime.GMT23
    time = ('GMT' + settleTime.clockFrom.substring(0, 2)) as ESettleTime
    return SettleTimeMap[time]
  }

  private parseBoolToString(isSupportDualMessage: boolean): string {
    return !!isSupportDualMessage ? '1' : '0'
  }

  private parseStringToBool(str: string): boolean {
    return !!(+str)
  }

  private labelFeature(): string {
    return this.trans('BIZ.ACQUIRER.ACQUIRER_FUNC.SINGLE_MESSAGE') + '/' + this.trans('BIZ.ACQUIRER.ACQUIRER_FUNC.DUAL_MESSAGE')
  }
}
